/*
 * File    : ShareResourceDirContentsImpl.java
 * Created : 02-Jan-2004
 * By      : parg
 * 
 * Azureus - a Java Bittorrent client
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details ( see the LICENSE file ).
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.gudy.azureus2.pluginsimpl.local.sharing;

/**
 * @author parg
 *
 */

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.AEThread2;
import org.gudy.azureus2.core3.util.Constants;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.RandomUtils;
import org.gudy.azureus2.plugins.sharing.ShareException;
import org.gudy.azureus2.plugins.sharing.ShareResource;
import org.gudy.azureus2.plugins.sharing.ShareResourceDeletionVetoException;
import org.gudy.azureus2.plugins.sharing.ShareResourceDirContents;
import org.gudy.azureus2.plugins.sharing.ShareResourceListener;
import org.gudy.azureus2.plugins.sharing.ShareResourceWillBeDeletedListener;
import org.gudy.azureus2.plugins.torrent.TorrentAttribute;

public class ShareResourceDirContentsImpl extends ShareResourceImpl implements ShareResourceDirContents {
    private final File root;
    private final boolean recursive;
    private final byte[] personal_key;

    protected ShareResource[] children = new ShareResource[0];

    protected ShareResourceDirContentsImpl(ShareManagerImpl _manager, File _dir, boolean _recursive, boolean _personal, boolean _async_check)

    throws ShareException {
        super(_manager, ST_DIR_CONTENTS);

        root = _dir;
        recursive = _recursive;

        if (!root.exists()) {

            throw (new ShareException("Dir '" + root.getName() + "' not found"));
        }

        if (root.isFile()) {

            throw (new ShareException("Not a directory"));
        }

        personal_key = _personal ? RandomUtils.nextSecureHash() : null;

        // new resource, trigger processing

        if (_async_check) {

            new AEThread2("SM:asyncCheck", true) {
                public void run() {
                    try {
                        checkConsistency();

                    } catch (Throwable e) {

                        Debug.out("Failed to update consistency", e);
                    }
                }
            }.start();

        } else {

            checkConsistency();
        }
    }

    protected ShareResourceDirContentsImpl(ShareManagerImpl _manager, File _dir, boolean _recursive, Map _map)

    throws ShareException {
        super(_manager, ST_DIR_CONTENTS, _map);

        root = _dir;
        recursive = _recursive;

        // recovery - see comment below about not failing if dir doesn't exist...

        if (!root.exists()) {

            Debug.out("Dir '" + root.getName() + "' not found");

            // throw( new ShareException( "Dir '".concat(root.getName()).concat("' not found")));

        } else {

            if (root.isFile()) {

                throw (new ShareException("Not a directory"));
            }
        }

        personal_key = (byte[]) _map.get("per_key");

        // deserialised resource, checkConsistency will be called later to trigger sub-share adding
    }

    public boolean canBeDeleted()

    throws ShareResourceDeletionVetoException {
        for (int i = 0; i < children.length; i++) {

            if (!children[i].canBeDeleted()) {

                return (false);
            }
        }

        return (true);
    }

    protected void checkConsistency()

    throws ShareException {
        // ensure all shares are defined as per dir contents and recursion flag

        List kids = checkConsistency(root);

        if (kids != null) {

            children = new ShareResource[kids.size()];

            kids.toArray(children);

        } else {

            children = new ShareResource[0];
        }
    }

    protected List checkConsistency(File dir)

    throws ShareException {
        List kids = new ArrayList();

        File[] files = dir.listFiles();

        if (files == null || !dir.exists()) {

            // dir has been deleted

            // actually, this can be bad as some os errors (e.g. "too many open files") can cause the dir
            // to appear to have been deleted. However, we don't want to delete the share. So let's just
            // leave it around, manual delete required if deletion required.

            if (dir == root) {

                return (null);

            } else {

                manager.delete(this, true);
            }
        } else {

            for (int i = 0; i < files.length; i++) {

                File file = files[i];

                String file_name = file.getName();

                if (!(file_name.equals(".") || file_name.equals(".."))) {

                    if (file.isDirectory()) {

                        if (recursive) {

                            List child = checkConsistency(file);

                            kids.add(new shareNode(this, file, child));

                        } else {

                            try {
                                ShareResource res = manager.getDir(file);

                                if (res == null) {

                                    res = manager.addDir(this, file, personal_key != null);
                                }

                                kids.add(res);

                            } catch (Throwable e) {

                                Debug.printStackTrace(e);
                            }
                        }
                    } else {

                        try {
                            ShareResource res = manager.getFile(file);

                            if (res == null) {

                                res = manager.addFile(this, file, personal_key != null);
                            }

                            kids.add(res);

                        } catch (Throwable e) {

                            Debug.printStackTrace(e);
                        }
                    }
                }
            }

            for (int i = 0; i < kids.size(); i++) {

                Object o = kids.get(i);

                if (o instanceof ShareResourceImpl) {

                    ((ShareResourceImpl) o).setParent(this);
                } else {

                    ((shareNode) o).setParent(this);
                }
            }
        }

        return (kids);
    }

    protected void deleteInternal() {
        for (int i = 0; i < children.length; i++) {

            try {
                if (children[i] instanceof ShareResourceImpl) {

                    ((ShareResourceImpl) children[i]).delete(true);
                } else {

                    ((shareNode) children[i]).delete(true);

                }
            } catch (Throwable e) {

                Debug.printStackTrace(e);
            }
        }
    }

    protected void serialiseResource(Map map) {
        super.serialiseResource(map);

        map.put("type", new Long(getType()));

        map.put("recursive", new Long(recursive ? 1 : 0));

        try {
            map.put("file", root.toString().getBytes(Constants.DEFAULT_ENCODING));

        } catch (UnsupportedEncodingException e) {

            Debug.printStackTrace(e);
        }

        if (personal_key != null) {

            map.put("per_key", personal_key);
        }
    }

    protected static ShareResourceImpl deserialiseResource(ShareManagerImpl manager, Map map)

    throws ShareException {
        try {
            File root = new File(new String((byte[]) map.get("file"), Constants.DEFAULT_ENCODING));

            boolean recursive = ((Long) map.get("recursive")).longValue() == 1;

            ShareResourceImpl res = new ShareResourceDirContentsImpl(manager, root, recursive, map);

            return (res);

        } catch (UnsupportedEncodingException e) {

            throw (new ShareException("internal error", e));
        }
    }

    public String getName() {
        return (root.toString());
    }

    public File getRoot() {
        return (root);
    }

    public boolean isRecursive() {
        return (recursive);
    }

    public ShareResource[] getChildren() {
        return (children);
    }

    protected class shareNode implements ShareResourceDirContents {
        protected ShareResourceDirContents node_parent;
        protected File node;
        protected ShareResource[] node_children;

        protected shareNode(ShareResourceDirContents _parent, File _node, List kids) {
            node_parent = _parent;
            node = _node;

            node_children = new ShareResource[kids.size()];

            kids.toArray(node_children);

            for (int i = 0; i < node_children.length; i++) {

                Object o = node_children[i];

                if (o instanceof ShareResourceImpl) {

                    ((ShareResourceImpl) o).setParent(this);
                } else {

                    ((shareNode) o).setParent(this);

                }
            }
        }

        public ShareResourceDirContents getParent() {
            return (node_parent);
        }

        protected void setParent(ShareResourceDirContents _parent) {
            node_parent = _parent;
        }

        public int getType() {
            return (ShareResource.ST_DIR_CONTENTS);
        }

        public String getName() {
            return (node.toString());
        }

        public void setAttribute(TorrentAttribute attribute, String value) {
            for (int i = 0; i < node_children.length; i++) {

                node_children[i].setAttribute(attribute, value);
            }
        }

        public String getAttribute(TorrentAttribute attribute) {
            return (null);
        }

        public TorrentAttribute[] getAttributes() {
            return (new TorrentAttribute[0]);
        }

        public void delete()

        throws ShareResourceDeletionVetoException {
            throw (new ShareResourceDeletionVetoException(MessageText.getString("plugin.sharing.remove.veto")));
        }

        public void delete(boolean force)

        throws ShareException, ShareResourceDeletionVetoException {
            for (int i = 0; i < node_children.length; i++) {

                Object o = node_children[i];

                if (o instanceof ShareResourceImpl) {

                    ((ShareResourceImpl) o).delete(force);
                } else {

                    ((shareNode) o).delete(force);
                }
            }
        }

        public boolean canBeDeleted()

        throws ShareResourceDeletionVetoException {
            for (int i = 0; i < node_children.length; i++) {

                node_children[i].canBeDeleted();
            }

            return (true);
        }

        public File getRoot() {
            return (node);
        }

        public boolean isRecursive() {
            return (recursive);
        }

        public ShareResource[] getChildren() {
            return (node_children);
        }

        public void addChangeListener(ShareResourceListener l) {
        }

        public void removeChangeListener(ShareResourceListener l) {
        }

        public void addDeletionListener(ShareResourceWillBeDeletedListener l) {
        }

        public void removeDeletionListener(ShareResourceWillBeDeletedListener l) {
        }
    }
}
